#pragma once
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>              
#include<netinet/in.h>
#include<arpa/inet.h>
#include<cstring>
#include<sys/wait.h>
#include<pthread.h>
#include<functional>
#include<memory>

#include"Log.hpp"
#include"InetAddr.hpp"




namespace socket_ns
{
    class Socket;
    using SockSPtr=std::shared_ptr<Socket>;
    const static int gbacklog=8;

    enum
    {
        SOCKET_ERROR=1,
        BIND_ERROR,
        LISTEN_ERROR,
        ACCEPT_ERROR
    };
    class Socket
    {
    public:
        virtual void CreateSocketOrDie()=0;
        virtual void CreateBindOrDie(uint16_t port)=0;
        virtual void CreateListenOrDie(int backlog=gbacklog)=0;
        virtual SockSPtr Accepter(InetAddr *cliaddr)=0;
        virtual bool Connector(std::string peerip,uint16_t peerport)=0;
        virtual int Sockfd()=0;
        virtual void Close()=0;
        virtual ssize_t Recv(std::string *out)=0;
        virtual ssize_t Send(std::string &in)=0;

    public:
        void BulidListenSocket(uint16_t port)
            {
                CreateSocketOrDie();
                CreateBindOrDie(port);
                CreateListenOrDie();
            }
            bool BuildClient(std::string peerip,uint16_t peerport)
            {
                CreateSocketOrDie();
                return Connector(peerip,peerport);
            }
    };



    class TcpSocket:public Socket
    {
    public:
        TcpSocket()
        {}
        TcpSocket(int sockfd):_sockfd(sockfd)
        {}
        ~TcpSocket()
        {}
        void CreateSocketOrDie() override
        {
            _sockfd=socket(AF_INET,SOCK_STREAM,0);
            if(_sockfd<0)
            {
                LOG(FATAL,"create socket error\n");
                exit(SOCKET_ERROR);
            }
            LOG(INFO,"create socket succsee\n");
        }
        void CreateBindOrDie(uint16_t port) override
        {
            struct sockaddr_in local;
            local.sin_family=AF_INET;
            local.sin_port=htons(port);
            local.sin_addr.s_addr=INADDR_ANY;
            if(bind(_sockfd,(struct sockaddr*)&local,sizeof(local))<0)
            {

                LOG(FATAL,"bind error\n");
                exit(BIND_ERROR);
            }
            LOG(INFO,"bind success\n");

        }
        void CreateListenOrDie(int backlog) override
        {
            if(::listen(_sockfd,backlog)<0)
            {
                LOG(FATAL,"listen error\n");
                exit(LISTEN_ERROR);
            }
            LOG(INFO,"listen success\n");
        }
        SockSPtr Accepter(InetAddr *cliaddr) override
        {
            struct sockaddr_in client;
            socklen_t len=sizeof(client);
            int sockfd=::accept(_sockfd,(struct sockaddr*)&client,&len);
            *cliaddr=InetAddr(client);
            if(sockfd<0)
            {
                LOG(WARNING,"accept error sockfd:%d\n",_sockfd);
                return nullptr;
            }
            LOG(INFO,"accept success client ip:%s sockfd:%d\n",cliaddr->Ip().c_str(),cliaddr->Port());
            return std::make_shared<TcpSocket>(sockfd);//c++14
        }
        bool Connector(std::string peerip,uint16_t peerport) override
        {
            struct sockaddr_in peer;
            peer.sin_family=AF_INET;
            peer.sin_port=htons(peerport);
            ::inet_pton(AF_INET,peerip.c_str(),&peer.sin_addr);

            int n=::connect(_sockfd,(struct sockaddr*)&peer,sizeof(peer));
            if(n<0)
            {
                return false;
            }
            return true;
        }
        int Sockfd()
        {
            return _sockfd;
        }
        void Close()
        {
            if(_sockfd>0)
            {
                ::close(_sockfd);
            }
        }
        ssize_t Recv(std::string *out) override
        {
            char buffer[4096];
            ssize_t n=::recv(_sockfd,buffer,sizeof(buffer)-1,0);
            if(n>0)
            {
                buffer[n]=0;
                *out+=buffer;

            }
            return n;
        }
        ssize_t Send(std::string &in) override
        {
            ssize_t n=::send(_sockfd,in.c_str(),in.size(),0);
            return n;
        }
    private:
        int _sockfd;
    };

    // Socket *sock=new TcpSocket();
    // sock->BulidListenSocket();
    // class UdpSocket:public socket
    // {};
}